package fusion;

import java.io.Serializable;
import java.util.HashMap;

public class FusionEntry {
    int numFaults;
    int numObjs;
    int[] localCode;
    HashMap<Integer, Integer> mapObjSize;
    //Fusion fusion;
    FusionAdd fusion;
    int idx;
  
    public FusionEntry(int numObjs, int numFaults, int idx) {
        this.numFaults = numFaults;
        this.numObjs = numObjs;
        this.idx = idx;
        
        fusion = new FusionAdd(numObjs, numFaults);
        mapObjSize = new HashMap<Integer, Integer>();
    }
    public int[] getCode() {
        return localCode;
    }
    public Object[] getRecoveredData(int[] objIdx, int[][] code, HashMap<Integer, Serializable> mapPrimariesObj) {
        Object[] ret = new Object[objIdx.length];
        int[][] iaRet = new int[objIdx.length][];

        //System.err.println("in getRecoveredData");
        for(int i = 0; i < objIdx.length; ++i) {
            //System.err.println("recover obj: " + objIdx[i]);
        }
   
        int[] erasures = new int[numFaults + 1];
        int[][] primariesData = new int[numObjs][];
        
        FusionUtil.BugOn(idx > code.length - 1);
        code[idx] = localCode;
        int[][] rsCode = null;
        if(code != null && code[0] != null) {
            rsCode = new int[code[0].length][code.length]; 
            for(int i = 0; i < code[0].length; ++i) {
                for(int j = 0; j < code.length; ++j) {
                    if(i < code[j].length) {
                        rsCode[i][j] = code[j][i];
                    }
                }
            }
        }

        int numErasures = 0;
        for(int i = 0; i < numObjs; ++i) {
            if(!mapPrimariesObj.containsKey(i)) {
                erasures[numErasures++] = i;
            }
            else if(mapPrimariesObj.get(i) == null) {
                primariesData[i] = null;
            }
            else {
                byte[] baObj = FusionUtil.Object2ByteArray(mapPrimariesObj.get(i));
                primariesData[i] = FusionUtil.ByteArray2IntArray(baObj);
            }
            if(numErasures > numFaults) {
                //System.err.println("numErasures > numFaults" + numErasures + " > " + numFaults);
                FusionUtil.BugOn(true);
                return ret;
            }
        }
        for(int i = numErasures; i < numFaults + 1; ++i) {
            //System.err.println("erasures: " + i + ": " + erasures[i]);
            erasures[i] = -1;
        }

        for(int i = 0; i < iaRet.length; ++i) {
            System.out.println("objIdx: " + objIdx[i]);
            int iaLength = mapObjSize.get(objIdx[i]) / 4;
            if(mapObjSize.get(objIdx[i]) % 4 != 0) {
                iaLength++;
            }
            iaRet[i] = new int[iaLength];
        }

        int[] dataword = new int[numObjs];
        

        for(int i = 0; i < rsCode.length; ++i) {
            for(int j = 0; j < numObjs; ++j) {
                if(primariesData[j] == null) {
                    //System.err.println("primaries data is empty " + j);
                    System.out.println("dataword: " + dataword[j]);
                    dataword[j] = 0;
                }
                else if(i < primariesData[j].length) {
                    //System.err.println("primaries data is not empty " + j);
                    dataword[j] = primariesData[j][i];
                    System.out.println(rsCode.length + "\t" + primariesData[j].length + "\t" + i);
                    System.out.println("j: " + j + " data: " + dataword[j]);
                    System.out.println("dataword: " + dataword[j]);
                }
                else {
                    dataword[j] = 0;
                }
            }
            try {
                int[] data = fusion.getRecoveredData(rsCode[i], dataword, erasures);
                for(int k = 0; k < objIdx.length; ++k) {
                    if(iaRet[k].length > i) {
                        iaRet[k][i] = data[objIdx[k]];
                        //System.err.println("recover int: " + data[objIdx[k]]);
                    }
                }
            }
            catch(InterfaceException e) {
            }
        }
        for(int i = 0; i < objIdx.length; ++i) {
            byte[] baRet = FusionUtil.IntArray2ByteArray(iaRet[i], mapObjSize.get(objIdx[i]));
            System.out.println("byte array length: " + baRet.length);
            ret[i] = FusionUtil.ByteArray2Object(baRet);
            if(ret[i] == null) System.out.println("null obj");
        }
        return ret;
    }
    
    public void updateCodes(Serializable oldElement, Serializable newElement, int objIdx) {
        byte[] baOldElement = null;
        byte[] baNewElement = null;
        int[] iaOldElement = null;
        int[] iaNewElement = null;
        int maxLength = (localCode == null) ? 0 : localCode.length;
        
        if(oldElement == null && newElement == null) return; 

        if(oldElement != null) {
            baOldElement = FusionUtil.Object2ByteArray(oldElement);
            iaOldElement = FusionUtil.ByteArray2IntArray(baOldElement);
            maxLength = (maxLength < iaOldElement.length) ? iaOldElement.length : maxLength;
        }
        if(newElement != null) {
            baNewElement = FusionUtil.Object2ByteArray(newElement);
            iaNewElement = FusionUtil.ByteArray2IntArray(baNewElement);
            mapObjSize.put(objIdx, baNewElement.length);
            maxLength = (maxLength < iaNewElement.length) ? iaNewElement.length : maxLength;
        }
        else {
            mapObjSize.put(objIdx, 0);
        }
         
        if(localCode == null) {
            localCode = new int[maxLength];
            for(int i = 0; i < maxLength; ++i) {
                localCode[i] = 0; 
            }
        }
        if(localCode.length < maxLength) {
            int[] newCode = new int[maxLength];
            System.arraycopy(localCode, 0, newCode, 0, localCode.length);
            localCode = newCode;
        }
        for(int i = 0; i < maxLength; ++i) {
            int newVal = 0;
            int oldVal = 0;
            if(iaOldElement != null && i < iaOldElement.length) {
                oldVal = iaOldElement[i];
            }
            if(iaNewElement != null && i < iaNewElement.length) {
                newVal = iaNewElement[i];
            }
            try {
                localCode[i] = fusion.getUpdatedCode(localCode[i], idx, oldVal, newVal, objIdx);
                //System.err.println("encode int: " + localCode[i]);
            }
            catch(InterfaceException e) {
            }
        }
    }
    //public void updateCodeWords(Serializable oldElement, Serializable newElement, int idx) {
    //    byte[] baOldElement = null;
    //    byte[] baNewElement = null;
    //    int[] iaOldElement = null;
    //    int[] iaNewElement = null;
    //    int maxLength = (code == null) ? 0 : code.length;
    //    
    //    if(oldElement == null && newElement == null) return; 

    //    if(oldElement != null) {
    //        baOldElement = FusionUtil.Object2ByteArray(oldElement);
    //        iaOldElement = FusionUtil.ByteArray2IntArray(baOldElement);
    //        maxLength = (maxLength < iaOldElement.length) ? iaOldElement.length : maxLength;
    //    }
    //    if(newElement != null) {
    //        baNewElement = FusionUtil.Object2ByteArray(newElement);
    //        iaNewElement = FusionUtil.ByteArray2IntArray(baNewElement);
    //        mapObjSize.put(idx, baNewElement.length);
    //        maxLength = (maxLength < iaNewElement.length) ? iaNewElement.length : maxLength;
    //    }
    //     
    //    if(code == null) {
    //        code = new int[maxLength][numFaults];
    //        for(int i = 0; i < maxLength; ++i) {
    //            for(int j = 0; j < numFaults; ++j) {
    //                code[i][j] = 0; 
    //            }
    //        }
    //    }
    //    if(code.length < maxLength) {
    //        int[][] newCode = new int[maxLength][numFaults];
    //        System.arraycopy(code, 0, newCode, 0, code.length);
    //        code = newCode;
    //    }
    //    for(int i = 0; i < maxLength; ++i) {
    //        int newVal = 0;
    //        int oldVal = 0;
    //        if(iaOldElement != null && i < iaOldElement.length) {
    //            oldVal = iaOldElement[i];
    //        }
    //        if(iaNewElement != null && i < iaNewElement.length) {
    //            newVal = iaNewElement[i];
    //        }
    //        try {
    //            code[i] = fusion.getUpdatedCodeWords(code[i], oldVal, newVal, idx);
    //        }
    //        catch(InterfaceException e) {
    //        }
    //    }
    //}
}
